package edu.whu.object;

import edu.whu.exception.MyException;
import edu.whu.ioc.MiniApplicationContext;
import org.dom4j.Element;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class BeanDefinition {
    private String id;
    private String clazz;
    private List<PropertyDefinition> properties;

    public String getId() {
        return id;
    }

    public String getClazz() {
        return clazz;
    }

    public List<PropertyDefinition> getProperties() {
        return properties;
    }

    public void load(Element element) throws MyException {
        try {
            id = element.attribute("id").getValue();
            clazz = element.attribute("class").getValue();
            //找到该bean节点的property子元素,并将其信息存放在PropertyDefinition中
            List<Element> property = element.elements("property");
            properties = new ArrayList<>();
            for(Element e : property) {
                PropertyDefinition propertyDefinition = new PropertyDefinition();
                propertyDefinition.loadProperty(e);
                properties.add(propertyDefinition);
            }
        } catch (NullPointerException e) {
            throw new MyException(MyException.ErrorType.CLASS_NULL_ERROR,"class属性没有值");
        }
    }

    public Object createObject(MiniApplicationContext mac) throws MyException {
        if(clazz == null) {
            throw new MyException(MyException.ErrorType.CLASS_NULL_ERROR,"class属性没有值");
        }
        try {
            //创建该对象实例
            Class cls = Class.forName(clazz);
            Object instance = cls.newInstance();
            //利用properties为对象中的字段初始化
            for(PropertyDefinition p : properties) {
                String pName = p.getName();
                //将首字母大写,并与set拼接，获得初始化方法的名字（目前仅实现使用set方法注入）
                String methodName = "set" + pName.substring(0,1).toUpperCase()+pName.substring(1);
                for (Method method : cls.getMethods()) {
                    //判断是否有名字相同的方法
                    if(method.getName().equals(methodName)) {
                        //如果有的话就判断property标签中是否有ref，如果有就使用ref的值，如果没有就是用value的值，如果都没有则不操作
                        Class parameterType = method.getParameterTypes()[0];
                        String pRef = p.getRef();
                        String pValue = p.getValue();
                        if(pRef != null) {
                            //从容器中找到对应的对象
                            method.invoke(instance,parameterType.cast(mac.getBean(pRef)));
                        }
                        else if(pValue != null) {
                            //将value的值赋给该字段
                            Object o = stringToTarget(pValue, parameterType);
                            method.invoke(instance, o);
                        }
                    }
                }
            }
            return instance;
        } catch (ClassNotFoundException e) {
            throw new MyException(MyException.ErrorType.CLSS_NOT_FOUND,"没有找到对应的类");
        } catch (InstantiationException e) {
            throw new MyException(MyException.ErrorType.INSTANCE_ERROR,"创建容器实例错误");
        } catch (IllegalAccessException e) {
            throw new MyException(MyException.ErrorType.INSTANCE_ERROR,"创建容器实例错误");
        } catch (InvocationTargetException e) {
            throw new MyException(MyException.ErrorType.METHOD_INVOKE_ERROR,"方法调用失败，为对象字段初始化失败");
        } catch (Exception e) {
            throw new MyException(MyException.ErrorType.ERROR_TYPE,"字符串类型转换错误");
        }
    }

    //将字符串转换为其他的基础类型
    public static Object stringToTarget(String string, Class<?> t) throws Exception {
        boolean nullOrEmpty = (string == null || string.equals(""));

        if (double.class.equals(t)) {
            return nullOrEmpty ? 0 : Double.parseDouble(string);
        } else if (long.class.equals(t)) {
            return nullOrEmpty ? 0 : Long.parseLong(string);
        } else if (int.class.equals(t)) {
            return nullOrEmpty ? 0 : Integer.parseInt(string);
        } else if (float.class.equals(t)) {
            return nullOrEmpty ? 0 : Float.parseFloat(string);
        } else if (short.class.equals(t)) {
            return nullOrEmpty ? 0 : Short.parseShort(string);
        } else if (boolean.class.equals(t)) {
            return nullOrEmpty ? 0 : Boolean.parseBoolean(string);
        }  else if (Number.class.isAssignableFrom(t)) {
            return t.getConstructor(String.class).newInstance(nullOrEmpty?"0":string);
        }    else {
            return nullOrEmpty ? "" : t.getConstructor(String.class).newInstance(string);
        }

    }
}
